'''
Created on 2011-07-13

@author: Mikael
'''

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import math
import PyQt4
import string

class MenuPie(object):
    '''
    The base class of all pie menus
    '''
    
    def __init__(self):
        '''
        Initliaze
        '''
        self.mAngle         = 0
        self.mStartAngle    = 0
        self.mIsIntersected = 0;
        self.mPath          = None
        self.mIsPressed     = 0
        self.mLinePen       = QPen(Qt.white, 1, Qt.SolidLine);
        
    def drawPie(self, QPainter, CircularMenu):
        '''
        Draw the pie with a gradient
        '''
    def drawBackground(self, QPainter, CircularMenu):
        QPainter.setPen(Qt.NoPen)
        #QPainter.setPen(self.mLinePen)
        QPainter.setBrush(Qt.NoBrush)
        
        #####################
        # Calculate math info
        #####################
        midleAngle = self.mStartAngle + (self.mAngle / 2)
        midleAngle /= 16 # To degree
        midleAngle = math.radians(midleAngle)
        
        startAngle = math.radians(self.mStartAngle / 16)
        
        endAngle = math.radians((self.mStartAngle + self.mAngle) / 16)
        
        xUnitMiddle = math.cos(midleAngle)
        yUnitMiddle = math.sin(midleAngle)
        
        xUnitStart = math.cos(startAngle)
        yUnitStart = math.sin(startAngle)
        
        xUnitEnd = math.cos(endAngle)
        yUnitEnd = math.sin(endAngle)
        
        x = xUnitStart * CircularMenu.mThreshold
        y = yUnitStart * CircularMenu.mThreshold
        
        ##################
        # Create pie path
        ##################
        painterPath = QPainterPath()
        self.mPath = painterPath
        
        
        center  = QRectF(CircularMenu.mCenterX - CircularMenu.mThreshold, 
                         CircularMenu.mCenterY - CircularMenu.mThreshold,
                         CircularMenu.mThreshold * 2,
                         CircularMenu.mThreshold * 2)  
        
        rectangle = QRectF(CircularMenu.SIZE_THRESHOLD_OVER_2, CircularMenu.SIZE_THRESHOLD_OVER_2, 
                           CircularMenu.mSize, CircularMenu.mSize)
        
        
        painterPath.moveTo(CircularMenu.mCenterX + xUnitStart * CircularMenu.mThreshold,
                           CircularMenu.mCenterY + yUnitStart * -CircularMenu.mThreshold)
        
        painterPath.arcTo(center, self.mStartAngle / 16, self.mAngle / 16)
        
        
        painterPath.arcTo(rectangle, (self.mStartAngle +self.mAngle)  / 16, -self.mAngle / 16)
        
        ###########
        # GRADIENT
        ###########
        gradientRadial = QRadialGradient();
        gradientRadial.setCenter(CircularMenu.mCenterX, CircularMenu.mCenterY);
        gradientRadial.setFocalPoint(CircularMenu.mCenterX, CircularMenu.mCenterY)
        gradientRadial.setRadius(CircularMenu.mRadius)
        
        alphaMul = CircularMenu.mSHAlphaMul
        
        gradientRadial.setColorAt(0.0,QColor(1,1,1, 0 * alphaMul));
        
        gradientRadial.setColorAt(0.2,QColor(50,50,50, 200 * alphaMul));
        gradientRadial.setColorAt(0.2,QColor(20,20,20, 200 * alphaMul));
        
        if self.mIsIntersected or self.mIsPressed:
            #gradientRadial.setColorAt(0.2,QColor(150,170,200, 200));
            gradientRadial.setColorAt(0.2,QColor(100,150,250, 230 * alphaMul))
        
        if self.mIsPressed:
            #gradientRadial.setColorAt(0.9,QColor(150,150,150));
            gradientRadial.setColorAt(0.75,QColor(50,50,50 , 255 * alphaMul));
        else:
            #gradientRadial.setColorAt(0.8,QColor(150,150,150));
            gradientRadial.setColorAt(0.85,QColor(50,50,50 , 255 * alphaMul));
            
        gradientRadial.setColorAt(0.95,QColor(180,180,180, 200 * alphaMul));
        gradientRadial.setColorAt(1.0,QColor(180,180,180, 0 * alphaMul));

        
        oldBrush = QPainter.brush()
        QPainter.setBrush(gradientRadial);
        QPainter.drawPath(painterPath)
        QPainter.setBrush(oldBrush);
        
        QPainter.setPen(self.mLinePen)
        QPainter.drawLine(CircularMenu.mCenterX + xUnitStart * CircularMenu.mThreshold,
                          CircularMenu.mCenterY + yUnitStart * -CircularMenu.mThreshold,
                          CircularMenu.mCenterX + xUnitStart * CircularMenu.mRadius,
                          CircularMenu.mCenterY + yUnitStart * -CircularMenu.mRadius)
        
        QPainter.drawEllipse(CircularMenu.mCenterX - CircularMenu.mThreshold,
                             CircularMenu.mCenterY - CircularMenu.mThreshold,
                             CircularMenu.mThreshold * 2,
                             CircularMenu.mThreshold * 2)
        
        
    
class TextMenuPie(MenuPie):
    
    def __init__(self, text, fontSize=12):
        MenuPie.__init__(self)
        self.mText          = text
        self.mTextPen       = QPen(Qt.white, 3, Qt.SolidLine);
        self.mFont          = QFont()
        self.mFont.setPixelSize(fontSize)
        self.mFont.setBold(1)
        

    def drawPie(self, QPainter, CircularMenu):
        '''
        Draw the menu text
        '''
        MenuPie.drawPie(self, QPainter, CircularMenu)
        
        #####################
        # Calculate math info
        #####################
        midleAngle = self.mStartAngle + (self.mAngle / 2)
        midleAngle /= 16 # To degree
        midleAngle = math.radians(midleAngle)
        
        startAngle = math.radians(self.mStartAngle / 16)
        
        endAngle = math.radians((self.mStartAngle + self.mAngle) / 16)
        
        xUnitMiddle = math.cos(midleAngle)
        yUnitMiddle = math.sin(midleAngle)
        
        xUnitStart = math.cos(startAngle)
        yUnitStart = math.sin(startAngle)

        
        #########
        # TEXT
        #########
        
        # metrics
        QPainter.setFont(self.mFont)
        QPainter.setPen(self.mTextPen)
        metrics = QPainter.fontMetrics()
        
        init = metrics.boundingRect(self.mText)
        cc = self.mText.count("\n") + 1
        

        middleX = ( xUnitMiddle * (CircularMenu.mRadius / 2) ) + xUnitMiddle * CircularMenu.mThreshold * 0.5
        middleY = ( yUnitMiddle * (CircularMenu.mRadius / 2) ) + yUnitMiddle * CircularMenu.mThreshold * 0.5

        textRec = QRectF(CircularMenu.mCenterX + middleX - init.width() / 2, 
                         CircularMenu.mCenterY - middleY - (init.height() * cc / 2), 
                         init.width(), init.height() * cc)    
        
        QPainter.setFont(self.mFont)
        QPainter.setPen(self.mTextPen)
        QPainter.drawText(textRec, Qt.AlignCenter, self.mText);
        
        self.mIsIntersected = 0
        

class ImageMenuPie(MenuPie):
    
    def __init__(self, QImage, width, height, hint="", imageSelected=None, imagePressed = None):
        MenuPie.__init__(self)
        self.mImage = QImage;
        self.mWidth = width
        self.mHeight = height
        self.mImageSelected = imageSelected
        self.mImagePressed = imagePressed
        
        
        self.mTextPen       = QPen(Qt.white, 3, Qt.SolidLine);
        self.mFont          = QFont()
        self.mFont.setPixelSize(12)
        self.mFont.setBold(1)
        self.mHint = hint
        
        
    def drawPie(self, QPainter, CircularMenu):
        '''
        Draw the menu image
        '''
        MenuPie.drawPie(self, QPainter, CircularMenu)
        
        #####################
        # Calculate math info
        #####################
        midleAngle = self.mStartAngle + (self.mAngle / 2)
        midleAngle /= 16 # To degree
        midleAngle = math.radians(midleAngle)
        
        startAngle = math.radians(self.mStartAngle / 16)
        
        endAngle = math.radians((self.mStartAngle + self.mAngle) / 16)
        
        xUnitMiddle = math.cos(midleAngle)
        yUnitMiddle = math.sin(midleAngle)
        
        xUnitStart = math.cos(startAngle)
        yUnitStart = math.sin(startAngle)
        
        xUnitEnd = math.cos(endAngle)
        yUnitEnd = math.sin(endAngle)
        
        x = xUnitStart * CircularMenu.mThreshold
        y = yUnitStart * CircularMenu.mThreshold
        middleX = ( xUnitMiddle * (CircularMenu.mRadius / 2) ) + xUnitMiddle * CircularMenu.mThreshold * 0.5
        middleY = ( yUnitMiddle * (CircularMenu.mRadius / 2) ) + yUnitMiddle * CircularMenu.mThreshold * 0.5
        
        
        ##################
        # Draw image
        ##################
        target = QRectF(CircularMenu.mCenterX + middleX - self.mWidth / 2, 
                         CircularMenu.mCenterY - middleY - (self.mHeight / 2), 
                         self.mWidth, self.mHeight) 
        
        source = QRectF(0, 
                        0, 
                        self.mImage.width(), 
                        self.mImage.height()) 

        if (not self.mIsIntersected or self.mImageSelected == None) and not self.mIsPressed:
            QPainter.drawImage(target, self.mImage, source)
        elif self.mIsPressed:
            QPainter.drawImage(target, self.mImagePressed, source)
        else:
            QPainter.drawImage(target, self.mImageSelected, source)
            self.drawHint(QPainter, target)
        

        self.mIsIntersected = 0
        
    def drawHint(self, QPainter, imageRec):
        #########
        # TEXT
        #########
        
        # metrics
        QPainter.setFont(self.mFont)
        QPainter.setPen(self.mTextPen)
        metrics = QPainter.fontMetrics()
        
        init = metrics.boundingRect(self.mHint)
        
        textRec = QRectF((imageRec.width() - init.width()) / 2 + imageRec.x(), 
                         imageRec.y() + imageRec.height() + 1, 
                         init.width(), init.height())    
        
        
        brush = QBrush(QColor(0,0,0,150))
        
        QPainter.setPen(Qt.NoPen)
        QPainter.setBrush(brush)
        QPainter.drawRect(textRec)
        
        
        
        QPainter.setFont(self.mFont)
        QPainter.setPen(self.mTextPen)
        QPainter.drawText(textRec, Qt.AlignCenter, self.mHint);
        

class CircularMenu(QWidget):
    '''
    A circular menu widget
    '''
    SIZE_THRESHOLD = 10
    SIZE_THRESHOLD_OVER_2 = SIZE_THRESHOLD / 2
    
    mSignalPressed = pyqtSignal(MenuPie)

    def __init__(self, size, threshold, parent=None):
        '''
        Constructor
        '''
        super(CircularMenu, self).__init__(parent)
        self.mParent = parent
        self.setParent(parent)
        print(parent)
        
        
        
        
        #self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint |  Qt.SplashScreen )
        #self.setWindowFlags(Qt.FramelessWindowHint |  Qt.Popup )
        self.setWindowFlags( Qt.FramelessWindowHint |  Qt.ToolTip )
        self.setAttribute(Qt.WA_TranslucentBackground);
        self.mMenuPieList = []
        
        
        self.setMenuSize(size, threshold)
        self.setMouseTracking(1)
        
        self.setCursor(Qt.PointingHandCursor)
        self.mSpawnTimerId = -1
        self.mSHAlphaMul = 0;
        '''
        self.mEffect = QGraphicsDropShadowEffect  ()
        self.mEffect.setBlurRadius(5)
        self.mEffect.setXOffset(0)
        self.mEffect.setYOffset(0)
        #self.mEffect.setOpacity(0.8)

        #self.mEffect.
        self.setGraphicsEffect(self.mEffect)'''
        self.setMouseTracking(1)
        #self.activateWindow()
        self.isLeave = 0
        
        # Linux hack
        #self.overrideWindowFlags(Qt.Popup)
        
        
        #self.mouseMoveEvent().connect(self.onMouseMove)
        
    def mouseMoveEvent(self, QMouseEvent):
        hitFound = 0
        for MenuPie in self.mMenuPieList:
            if MenuPie.mPath.contains(QPointF(QMouseEvent.x(), QMouseEvent.y())):
                MenuPie.mIsIntersected = 1
                self.repaint()
                hitFound = 1
                
        if not hitFound:
            self.isLeave = 1
            self.setCursor(Qt.ArrowCursor)
        else:
            self.isLeave = 0
            self.setCursor(Qt.PointingHandCursor)
            
        
    def leaveEvent (self, QEvent):
        # Linux hack
        print("leave")
        self.isLeave = 1
        for MenuPie in self.mMenuPieList:
            MenuPie.mIsIntersected = 0
            MenuPie.mIsPressed = 0
            self.repaint()

    def enterEvent(self, *args, **kwargs):
        # Linux hack
        print("enter")
        self.isLeave = 0
    def mousePressEvent(self, QMouseEvent):
        print("mouse click")
        # Linux hack
        print("leave")
        if self.isLeave :
            self.releaseMouse()
            self.hide()
        else:
            for MenuPie in self.mMenuPieList:
                if MenuPie.mPath.contains(QPointF(QMouseEvent.x(), QMouseEvent.y())):
                    MenuPie.mIsPressed = 1
                else:
                    MenuPie.mIsPressed = 0
            self.repaint()
            
    def mouseReleaseEvent(self, QMouseEvent):
        print("mouse release")
        hide = 0
        for MenuPie in self.mMenuPieList:
            if MenuPie.mPath.contains(QPointF(QMouseEvent.x(), QMouseEvent.y())):
                if MenuPie.mIsPressed:
                    self.mSignalPressed.emit(MenuPie)
                    hide = 1
                
                MenuPie.mIsPressed = 0
                MenuPie.mIsIntersected = 1
                
            else:
                MenuPie.mIsPressed = 0
        
        
        
        if hide:
            self.releaseMouse()
            self.hide()
        
        self.repaint()
        
        
        
        
    def setMenuSize(self, size, threashold):
        
        self.mThreshold = threashold
        self.mSize      = size
        self.mRadius    = size / 2
        self.mCenterX   = (size / 2) + self.SIZE_THRESHOLD_OVER_2
        self.mCenterY   = self.mCenterX
        
        self.resize(size + self.SIZE_THRESHOLD, size + self.SIZE_THRESHOLD)
        
    def paintEvent(self, QPaintEvent):
        self.updatePies()
        
    
        pen = QPen(Qt.black, 2, Qt.SolidLine);
        painter =  QPainter(self);
        painter.setRenderHint(QPainter.Antialiasing)

        painter.setPen(pen);
        
        for menu in self.mMenuPieList:
            MenuPie.drawBackground(menu, painter, self)
        
        for menu in self.mMenuPieList:
            menu.drawPie(painter, self)
            
        
    def updatePies(self):
        count = len(self.mMenuPieList)
        idx = 0
        for MenuPie in self.mMenuPieList:
            MenuPie.mStartAngle, MenuPie.mAngle = self.sepAlg(count, idx)
            idx +=1
            
        
    def createTextMenu(self, text, fontSize=12):
        '''
        Create and return a text menu pie
        '''
        
        menu = TextMenuPie(text, fontSize)
        self.mMenuPieList.append(menu)
        return menu
    
    def createImageMenu(self, QImage, width, height, hint="", imageWhenSelected=None, imageWhenPressed=None):
        '''
        Create and return an image menu pie
        '''
        menu = ImageMenuPie(QImage, width, height, hint, imageWhenSelected, imageWhenPressed)
        self.mMenuPieList.append(menu)
        return menu
        
    def sepAlg(self, menuCount, itemIdx):
        
        degreePerPie    = 360 / menuCount
        valPerPie       = degreePerPie * 16
        startingIdx     = itemIdx * valPerPie
        
        return startingIdx, valPerPie
        
    def spawn(self, x, y):
        #absolute = QPoint(x - self.mCenterX,y - self.mCenterY)
        
        self.mSHAlphaMul = 0
        '''if self.mParent:
            posi =  self.mapToGlobal(self.pos())
            #locale = self.mapFromGlobal(absolute)
            self.move(0,0)
        else:'''
        self.move(QPoint(x - self.mCenterX,y - self.mCenterY))
        self.show()
        self.setFocus()
        #self.activateWindow()
        self.mSpawnTimerId = self.startTimer(10)
        self.repaint()
        self.grabMouse()
        self.setCursor(Qt.ArrowCursor)
        
    def timerEvent(self, QTimerEvent):
        id = QTimerEvent.timerId()
        
        
        
        if id == self.mSpawnTimerId:
            self.mSHAlphaMul += 0.1
            if self.mSHAlphaMul >= 1:
                self.mSHAlphaMul = 1
                self.killTimer(self.mSpawnTimerId)
                 
            self.repaint()
    
        
    def focusOutEvent(self, QFocusEvent):
        self.hide()
        
        
    
        
        
        
        